package com.juma.cartridge.rlock;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.juma.cartridge.rlock.exception.LockException;

public abstract class RLock implements Lock,Delayed {
	
	private RedisLockManager manager;
	private AtomicBoolean locked = new AtomicBoolean(false);
	private volatile int reentry;
	private Thread holder;
	protected LockNode node;
	
	public RLock(RedisLockManager manager,LockNode node,Thread holder) {
		this.manager = manager;
		this.node = node;
		this.holder = holder;
	}
	
	@Override
	public String getKey() {
		return node.getKey();
	}

	@Override
	public String getId() {
		return node.getLockId();
	}
	
	public boolean isLocked() {
		return locked.get();
	}
	
	@Override
	public int getLockSeconds() {
		return node.getLockSeconds();
	}
	
	@Override
	public long getExpireTime() {
		return node.getExpireTime();
	}

	public LockNode getNode() {
		return node;
	}
	
	public boolean isExpired() {
		return getExpireTime() < System.currentTimeMillis();
	}
	
	@Override
	public void acquire(long retryWaitTimes) {
		if(!Thread.currentThread().equals(holder)) {
			throw new LockException("当前线程[id="+Thread.currentThread().getId()+"]非当前锁的持有者");
		}
		
		if(reentry==0) {
			try {
				acquireLock(retryWaitTimes);
				locked.set(true);
				manager.lockAcquired(getKey());	
			}catch(LockException le) {
			    manager.lockOver(getKey());
				throw le;
			}catch(Exception e) {
			    manager.lockOver(getKey());
				throw new LockException("获取锁失败:"+e.getMessage(),e);
			}
		}
		reentry++;
	}

	@Override
	public void release() {
		if(!Thread.currentThread().equals(holder)) {
			throw new LockException("当前线程[id="+Thread.currentThread().getId()+"]非当前锁的持有者");
		}
		
		try{
		    if(!locked.get()) {
	            return;
	        }
		    
	        reentry--;
	        if(reentry>0) {
	            return;
	        }
	        
			releaseLock();
		}catch(LockException le) {
			throw le;
		}catch(Exception e) {
			throw new LockException("释放锁异常:"+e.getMessage(), e);
		}finally {
			locked.set(false);
			manager.lockOver(getKey());
		}
	}

	@Override
	public int compareTo(Delayed o) {
		Lock ln = (Lock)o;
		long v = getExpireTime()-ln.getExpireTime();
		if(v==0) {
			return 0;
		}
		if(v<0) {
			return -1;
		}
		return 1; 
	}

	@Override
	public long getDelay(TimeUnit unit) {
		long delayMillis = getExpireTime()-System.currentTimeMillis()-2000;
		return unit.convert(delayMillis, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * 续租
	 */
	public abstract void lease();
	
	protected abstract void acquireLock(long retryWaitTimes);
	
	protected abstract void releaseLock();
	
	@Override
	public boolean equals(Object obj) {
		if (obj==this) {
			return true;
		}
		
		if (obj instanceof Lock) {
			Lock lock = (Lock)obj;
			return getId().equals(lock.getId());
		}
		return false;
	}
}
